home *** CD-ROM | disk | FTP | other *** search
- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Begin Listing4-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- /*****************************************************/
- /* listmenu.c */
- /* -- Implements a popup menu with a scrollable */
- /* ListBox. */
- /*****************************************************/
-
- /*****************************************************/
- /* Header files. */
- /*****************************************************/
- #include <windows.h>
- #include "listmenu.h"
-
- /*****************************************************/
- /* Constants. */
- /*****************************************************/
- #define imdsNil -1 /* Undefined menu index. */
- #define cbMenuMax 256 /* Max. length of MenuItem. */
- #define szMenuClass "#32768" /* Menu classname. */
- #define szListClass "ListBox" /* LBox classname. */
- #define szFilter "LFilter" /* Filter classname. */
-
- /*****************************************************/
- /* Types. */
- /*****************************************************/
- typedef struct
- {
- int cidm; /* # items in menu. */
- int dx, dy; /* Size of menu. */
- int idm; /* Index in MenuBar. */
- HMENU hmnu; /* Original popup menu. */
- HWND hwnd; /* ListBox window. */
- WORD rgidm[1]; /* MenuItem id array */
- } MDS; /* Menu DeScriptor. */
- typedef MDS FAR * LPMDS;
- typedef HANDLE HMDS;
- typedef HMDS FAR * LRGHMDS;
-
- /*****************************************************/
- /* Globals. */
- /*****************************************************/
-
- HANDLE hrghmds; /* Handle to array of menus. */
- int cmds; /* Number of menus. */
- int imdsDown; /* Dropped popup menu. */
- HWND hwndMenu; /* Popup menu window. */
- HWND hwndMain; /* App's main window. */
- HMENU hmnuTop; /* App's MenuBar. */
- FARPROC lpfnMenuFilter; /* Popup menu subclasser. */
- FARPROC lpfnMenu; /* Popup menu window proc. */
- FARPROC lpfnMain; /* App's main window proc. */
- FARPROC lpfnMainFilter; /* Subclasser for above. */
- FARPROC lpfnListFilter; /* Subclasser for ListBox. */
- FARPROC lpfnList;
- BOOL fButtonDown; /* Buttoned down in list? */
-
- /*****************************************************/
- /* Private Prototypes. */
- /*****************************************************/
- void CloseListMenu(void);
- void DestroyMenuImds(int);
- BOOL FAR PASCAL FEnumWnd(HWND, LONG);
- void GetMdsImds(MDS *, int);
- WORD IdmFromIidmImds(int, int);
- int ImdsFromIdm(WORD);
- LONG FAR PASCAL ListFilter(HWND, WORD, WORD, LONG);
- LONG FAR PASCAL MainFilter(HWND, WORD, WORD, LONG);
- LONG FAR PASCAL MenuFilter(HWND, WORD, WORD, LONG);
- void RemoveListMenu(BOOL);
-
- /*****************************************************/
- /* Routines. */
- /*****************************************************/
-
- BOOL
- FInitListMenu(BOOL fFirst, HWND hwnd)
- /*****************************************************/
- /* -- Initialize the list menu module. */
- /* -- hwnd : Window containing MenuBar. */
- /* -- szMenuList : Name of menu list of popups. */
- /*****************************************************/
- {
- FARPROC lpfn; /* EnumWindows() callback. */
- HANDLE hins; /* App's instance. */
- WNDCLASS wcs; /* Our own ListBox class. */
-
- hwndMain = hwnd;
- if ((hmnuTop = GetMenu(hwnd)) == NULL)
- return FALSE;
-
- /* Get the popup menu handle so we can subclass */
- /* it at will. */
- hins = GetWindowWord(hwnd, GWW_HINSTANCE);
- if ((lpfn = MakeProcInstance(FEnumWnd, hins)) ==
- NULL)
- return FALSE;
-
- EnumWindows(lpfn, 0L);
- FreeProcInstance(lpfn);
- if (hwndMenu == NULL)
- return FALSE;
-
- lpfnMenu =
- (FARPROC)GetWindowLong(hwndMenu, GWL_WNDPROC);
- if ((lpfnMenuFilter = MakeProcInstance(
- (FARPROC)MenuFilter, hins)) == NULL)
- return FALSE;
-
- /* Subclass the app's main window. */
- lpfnMain =
- (FARPROC)GetWindowLong(hwndMain, GWL_WNDPROC);
- if ((lpfnMainFilter = MakeProcInstance(
- (FARPROC)MainFilter, hins)) == NULL)
- return FALSE;
- SetWindowLong(hwndMain, GWL_WNDPROC,
- (LONG)lpfnMainFilter);
-
- /* Create a our version of the ListBox class. */
- if (!GetClassInfo(NULL, szListClass, &wcs))
- return FALSE;
- lpfnList = (FARPROC)wcs.lpfnWndProc;
-
- if (fFirst)
- {
- wcs.lpfnWndProc = ListFilter;
- wcs.hInstance = hins;
- wcs.lpszClassName = szFilter;
- if (!RegisterClass(&wcs))
- return FALSE;
- }
-
- imdsDown = imdsNil;
- return TRUE;
- }
-
- BOOL FAR PASCAL
- FEnumWnd(HWND hwnd, LONG lwp)
- /*****************************************************/
- /* -- EnumWindows() callback to get popup menu */
- /* window handle. */
- /*****************************************************/
- {
- char szBuf[40];
-
- GetClassName(hwnd, szBuf, sizeof szBuf);
- if (!lstrcmp(szBuf, szMenuClass))
- {
- hwndMenu = hwnd;
- return FALSE;
- }
- return TRUE;
- }
-
- void
- CloseListMenu(void)
- /*****************************************************/
- /* -- Close the list menu module. */
- /*****************************************************/
- {
- int imds;
-
- if (cmds == 0)
- return; /* Nothing to do. */
-
- /* Destroy all list menus. */
- for (imds = 0; imds < cmds; imds++)
- DestroyMenuImds(imds);
-
- /* Remove filters. */
- if (lpfnMenu != NULL)
- {
- if (IsWindow(hwndMenu))
- SetWindowLong(hwndMenu, GWL_WNDPROC,
- (LONG)lpfnMenu);
- lpfnMenu = NULL;
- }
- if (lpfnMenuFilter != NULL)
- {
- FreeProcInstance(lpfnMenuFilter);
- lpfnMenuFilter = NULL;
- }
-
- if (lpfnMain != NULL && IsWindow(hwndMain))
- SetWindowLong(hwndMain, GWL_WNDPROC,
- (LONG)lpfnMain);
- if (lpfnMainFilter != NULL)
- {
- FreeProcInstance(lpfnMainFilter);
- lpfnMainFilter = NULL;
- }
-
- /* Restore state and free menu table array. */
- hmnuTop = NULL;
- hwndMenu = hwndMain = NULL;
- GlobalFree(hrghmds);
- hrghmds = NULL;
- cmds = 0;
- }
-
- BOOL
- FAssignMenu(WORD idm, int dy)
- /*****************************************************/
- /* -- Assign a list menu to the given MenuItem. */
- /* -- The strings and command values for the list */
- /* menu are extracted from the given popup menu. */
- /* -- Return TRUE for success, FALSE for failure. */
- /* -- idm : MenuItem id. */
- /* -- dy : Height of the menu. */
- /*****************************************************/
- {
- int imds;
- HMENU hmnu;
- HMDS hmds = NULL;
- LPMDS lpmds = NULL;
- HDC hdc = NULL;
- int iidm, cidm;
- BOOL fVal = FALSE;
- HANDLE hins;
-
- /* See if a list menu for this popup already */
- /* exists. If not, grow (or allocate if for the */
- /* first time) the list menu table array. If */
- /* a list menu already exists, destroy it, but */
- /* reuse its slot in the array. */
- if ((imds = ImdsFromIdm(idm)) == imdsNil)
- {
- /* Grow menu array. */
- if (cmds == 0)
- {
- if ((hrghmds = GlobalAlloc(
- GMEM_MOVEABLE | GMEM_ZEROINIT,
- sizeof(HMDS))) == NULL)
- goto FAssignMenuError;
-
- cmds = 1;
- imds = 0;
- }
- else
- {
- HANDLE hrghmdsNew;
-
- imds = cmds++;
- if ((hrghmdsNew = GlobalReAlloc(hrghmds,
- cmds * sizeof(HMDS),
- GMEM_MOVEABLE | GMEM_ZEROINIT))
- == NULL)
- goto FAssignMenuError;
-
- hrghmds = hrghmdsNew;
- }
- }
- else
- {
- DestroyMenuImds(imds);
- }
-
- /* Create a new menu. Clone a ListBox with the */
- /* same MenuItems. */
- hins = GetWindowWord(hwndMain, GWW_HINSTANCE);
- if ((hmnu = GetSubMenu(hmnuTop, idm)) == NULL)
- goto FAssignMenuError;
-
- if ((cidm = GetMenuItemCount(hmnu)) < 0)
- goto FAssignMenuError;
-
- if ((hmds = GlobalAlloc(GMEM_MOVEABLE,
- sizeof(MDS) + (cidm - 1) * sizeof(WORD))) ==
- NULL)
- goto FAssignMenuError;
-
- lpmds = (LPMDS)GlobalLock(hmds);
- lpmds->cidm = cidm;
- lpmds->hmnu = hmnu;
- lpmds->idm = idm;
- if ((lpmds->hwnd = CreateWindow(szFilter, NULL,
- WS_CHILD | WS_BORDER | WS_VSCROLL | WS_VISIBLE,
- 0, 0, 0, 0, hwndMain, idm, hins, NULL)) == NULL)
- goto FAssignMenuError;
-
- /* Fill the ListBox with MenuItems. Get width */
- /* of widest item. */
- lpmds->dx = -1;
- if ((hdc = GetDC(lpmds->hwnd)) == NULL)
- goto FAssignMenuError;
-
- for (iidm = 0; iidm < cidm; iidm++)
- {
- char sz[cbMenuMax];
- int dx;
-
- if ((lpmds->rgidm[iidm] =
- GetMenuItemID(hmnu, iidm)) == -1)
- goto FAssignMenuError;
-
- if (GetMenuString(hmnu, iidm, sz, sizeof sz,
- MF_BYPOSITION) <= 0)
- goto FAssignMenuError;
- if (SendMessage(lpmds->hwnd, LB_ADDSTRING, 0,
- (LONG)(LPSTR)sz) == LB_ERR)
- goto FAssignMenuError;
- dx = LOWORD(GetTextExtent(hdc, sz,
- lstrlen(sz)));
- if (dx > lpmds->dx)
- lpmds->dx = dx;
- }
-
- /* Allow for scroll bar and extra whitespace. */
- lpmds->dx += 2 * GetSystemMetrics(SM_CXVSCROLL);
- lpmds->dy = dy;
- ((LRGHMDS)GlobalLock(hrghmds))[imds] = hmds;
- GlobalUnlock(hrghmds);
-
- fVal = TRUE;
- goto FAssignMenuExit;
-
- FAssignMenuError: /* Clean up in case of error. */
- if (lpmds != NULL && lpmds->hwnd != NULL)
- DestroyWindow(lpmds->hwnd);
- if (hmds != NULL)
- GlobalFree(hmds);
-
- FAssignMenuExit: /* Normal cleanup. */
- if (hdc != NULL)
- ReleaseDC(lpmds->hwnd, hdc);
- if (lpmds != NULL)
- GlobalUnlock(hmds);
-
- return fVal;
- }
-
- int
- ImdsFromIdm(WORD idm)
- /*****************************************************/
- /* -- Given a top-level MenuItem id, return the */
- /* associated list menu. */
- /* -- Return -1 if no such menu. */
- /*****************************************************/
- {
- int imds;
- MDS mds;
-
- if (cmds == 0)
- return imdsNil;
-
- for (imds = 0; imds < cmds; imds++)
- {
- GetMdsImds(&mds, imds);
- if (mds.hwnd != NULL &&
- GetWindowWord(mds.hwnd, GWW_ID) == idm)
- break;
- }
-
- return imds == cmds ? imdsNil : imds;
- }
-
- void
- DestroyMenuImds(int imds)
- /*****************************************************/
- /* -- Destroy the given list menu. */
- /* -- imds : Index of menu. */
- /*****************************************************/
- {
- LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
- HMDS hmds = lrghmds[imds];
- LPMDS lpmds = (LPMDS)GlobalLock(hmds);
-
- if (IsWindow(lpmds->hwnd))
- DestroyWindow(lpmds->hwnd);
-
- GlobalUnlock(hmds);
- GlobalFree(hmds);
- lrghmds[imds] = NULL;
- GlobalUnlock(hrghmds);
- }
-
- LONG FAR PASCAL
- MenuFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
- /*****************************************************/
- /* -- Subclasser for menu popup window. */
- /*****************************************************/
- {
- switch (wm)
- {
- default:
- break;
-
- case WM_PAINT:
- {
- PAINTSTRUCT wps;
- RECT rect;
- MDS mds;
-
- /* Eat the paint message and remove the */
- /* popup menu and subclasser. */
- BeginPaint(hwnd, &wps);
- EndPaint(hwnd, &wps);
- ShowWindow(hwnd, SW_HIDE);
- SetWindowLong(hwndMenu, GWL_WNDPROC,
- (LONG)lpfnMenu);
-
- /* Position and display the list menu. */
- GetMdsImds(&mds, imdsDown);
- GetWindowRect(hwnd, &rect);
- ScreenToClient(hwndMain, (LPPOINT)&rect);
- MoveWindow(mds.hwnd,
- rect.left, rect.top, mds.dx, mds.dy, TRUE);
- SendMessage(mds.hwnd, LB_SETCURSEL, 0, 0L);
- SetFocus(mds.hwnd);
-
- /* Get out of menu mode. */
- PostMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 0L);
- PostMessage(hwndMain, WM_KEYDOWN, VK_ESCAPE,
- 0L);
-
- /* But highlight the top-level MenuItem to */
- /* look like we are still in it. Have to do */
- /* this after the menu manager has removed */
- /* it, though!. So post a message to */
- /* ourself that will arrive after. */
- PostMessage(hwndMain, WM_USER, 0, 0L);
- }
- return 0L;
-
- case WM_ERASEBKGND:
- return 1L; /* Eat this message too. */
- } /* End switch wm. */
-
- return CallWindowProc(lpfnMenu, hwnd, wm, wp, lwp);
- }
-
- LONG FAR PASCAL
- MainFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
- /*****************************************************/
- /* -- Subclasser for app's main window. */
- /*****************************************************/
- {
- switch (wm)
- {
- default:
- break;
-
- case WM_INITMENUPOPUP:
- /* Get rid of any list menu already visible. */
- RemoveListMenu(FALSE);
-
- /* Install the menu filter to start the ball */
- /* rolling. */
- if (HIWORD(lwp) == 0 &&
- (imdsDown = ImdsFromIdm(LOWORD(lwp))) !=
- imdsNil)
- SetWindowLong(hwndMenu, GWL_WNDPROC,
- (LONG)lpfnMenuFilter);
- break;
-
- case WM_COMMAND:
- /* Eat notifications from the menu */
- /* ListBoxes. */
- if (ImdsFromIdm(wp) != imdsNil)
- return 0L;
-
- case WM_ACTIVATE:
- case WM_LBUTTONDOWN:
- case WM_NCLBUTTONDOWN:
- /* If the user button's down elsewhere in */
- /* the app, or activates another app, remove */
- /* any visisble list menu. */
- RemoveListMenu(FALSE);
- break;
-
- case WM_USER:
- if (imdsDown != imdsNil)
- {
- MDS mds;
-
- GetMdsImds(&mds, imdsDown);
- HiliteMenuItem(hwnd, hmnuTop, mds.idm,
- MF_BYPOSITION | MF_HILITE);
- }
- break;
-
- case WM_DESTROY:
- CloseListMenu(); /* Clean up. */
- break;
- } /* End switch wm. */
-
- return CallWindowProc(lpfnMain, hwnd, wm, wp, lwp);
- }
-
- void
- RemoveListMenu(BOOL fCommand)
- /*****************************************************/
- /* -- If a list menu is visible, remove it. */
- /* -- fCommand : Generate a menu-style WM_COMMAND */
- /* message if set. */
- /*****************************************************/
- {
- MDS mds;
- int imdsSav;
-
- fButtonDown = FALSE;
- if (imdsDown == imdsNil)
- return;
-
- GetMdsImds(&mds, imdsDown);
- HiliteMenuItem(hwndMain, hmnuTop, mds.idm,
- MF_BYPOSITION | MF_UNHILITE);
- imdsSav = imdsDown;
- imdsDown = imdsNil;
-
- /* Remove focus first, so there isn't a ghost */
- /* caret floating around until the parent */
- /* windows gets a change to repaint itself. */
- if (GetFocus() == mds.hwnd)
- SetFocus(hwndMain);
-
- /* Hide the list menu. */
- MoveWindow(mds.hwnd, 0, 0, 0, 0, TRUE);
-
- if (fCommand) /* Notify the app. */
- {
- int iidm;
-
- iidm = (int)SendMessage(mds.hwnd, LB_GETCURSEL,
- 0, 0L);
- if (iidm != LB_ERR)
- PostMessage(hwndMain, WM_COMMAND,
- IdmFromIidmImds(iidm, imdsSav), 0L);
- }
- }
-
- LONG FAR PASCAL
- ListFilter(HWND hwnd, WORD wm, WORD wp, LONG lwp)
- /*****************************************************/
- /* -- Subclasser for ListBox control. */
- /*****************************************************/
- {
- switch (wm)
- {
- default:
- break;
-
- case WM_LBUTTONDOWN:
- fButtonDown = TRUE;
- break;
-
- case WM_LBUTTONUP:
- if (fButtonDown)
- {
- LONG pt = GetMessagePos();
- RECT rect;
-
- /* Do not generate a command if the user */
- /* releases the mouse outside the bounds */
- /* of the ListBox. */
- GetClientRect(hwnd, &rect);
- ScreenToClient(hwnd, (LPPOINT)&pt);
- RemoveListMenu(
- PtInRect(&rect, *(POINT *)&pt));
- }
- break;
-
- case WM_SIZE:
- {
- LONG lVal;
-
- /* Windows will put a bogus horizontal */
- /* scroll bar style in the window in */
- /* response to this message! So stomp it */
- /* out. */
- lVal =
- CallWindowProc(lpfnList, hwnd, wm, wp, lwp);
- SetWindowLong(hwnd, GWL_STYLE,
- GetWindowLong(hwnd, GWL_STYLE) & ~WS_HSCROLL);
- return lVal;
- }
-
- case WM_KEYDOWN:
- switch (wp)
- {
- default:
- break;
-
- case VK_RETURN:
- RemoveListMenu(TRUE);
- return 0L;
-
- case VK_ESCAPE:
- RemoveListMenu(FALSE);
- return 0L;
- } /* End switch wp. */
- break; /* End case WM_KEYDOWN. */
- } /* End switch wm. */
-
- return CallWindowProc(lpfnList, hwnd, wm, wp, lwp);
- }
-
- void
- GetMdsImds(MDS * pmds, int imds)
- /*****************************************************/
- /* -- Given a list menu id, return its mds data. */
- /* -- pmds : Fill this struct. */
- /* -- imds : Index of list menu in array. */
- /*****************************************************/
- {
- LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
-
- *pmds = *(LPMDS)GlobalLock(lrghmds[imds]);
- GlobalUnlock(lrghmds[imds]);
- GlobalUnlock(hrghmds);
- }
-
- WORD
- IdmFromIidmImds(int iidm, int imds)
- /*****************************************************/
- /* -- Return the requested MenuItem id. */
- /* -- iidm : Index of MenuItem id in arrar of them. */
- /* -- imds : Index of list menu in array of them. */
- /*****************************************************/
- {
- LRGHMDS lrghmds = (LRGHMDS)GlobalLock(hrghmds);
- WORD idm;
-
- idm =
- ((LPMDS)GlobalLock(lrghmds[imds]))->rgidm[iidm];
- GlobalUnlock(lrghmds[imds]);
- GlobalUnlock(hrghmds);
- return idm;
- }
- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=End Listing4-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-